package evaluator

import (
	"fmt"
	"fuck/ast"
	"fuck/object"
)

var ( //写一个true和false的单例
	NULL  = &object.Null{}
	TRUE  = &object.Boolean{Value: true}
	FALSE = &object.Boolean{Value: false}
)

func Eval(node ast.Node, env *object.Environment) object.Object { //返回值为接口时，可以返回实现该接口的struct，也可以返回struct指针
	switch node := node.(type) { //递归调用自己来求值
	case *ast.Program:
		return evalProgram(node, env)
	case *ast.ExpressionStatement:
		return Eval(node.Expression, env)
	case *ast.Identifier:
		return evalIdentifier(node, env)
	case *ast.IntegerLiteral:
		return &object.Integer{Value: node.Value}
	case *ast.Boolean:
		return nativeBoolToBooleanObject(node.Value)
	case *ast.PrefixExpression:
		right := Eval(node.Right, env)
		if isError(right) {
			return right
		}
		return evalPrefixExpression(node, right)
	case *ast.InfixExpression:
		left := Eval(node.Left, env)
		right := Eval(node.Right, env)
		if isError(left) {
			return left
		}
		if isError(right) {
			return right
		}
		return evalInfixExpression(node, left, right)
	case *ast.BlockStatement:
		return evalBlockStatement(node, env)
		//evalStatements(node.Statements)
	case *ast.IfExpression:
		return evalIfExpression(node, env)
	case *ast.ReturnStatement:
		val := Eval(node.ReturnValue, env)
		return &object.ReturnValue{Value: val}
	case *ast.LetStatement:
		val := Eval(node.Value, env)
		if isError(val) {
			return val
		}
		env.Set(node.Name.Value, val)
	case *ast.FunctionLiteral:
		params := node.Parameters
		body := node.Body
		return &object.Function{Parameters: params, Env: env, Body: body} //对函数字面量求值时，获得object.Function,并在其Env为当前环境的闭包
	case *ast.CallExpression:
		function := Eval(node.Function, env) //通过()左边的Identifier或者FunctionLiteral得到*object.Function
		if isError(function) {
			return function
		}
		args := evalExpressions(node.Arguments, env)
		if len(args) == 1 && isError(args[0]) {
			return args[0]
		}
		return applyFunction(function, args, node.Token.LineNum)
	case *ast.StringLiteral:
		return &object.String{Value: node.Value}
	case *ast.ArrayLiteral:
		elements := evalExpressions(node.Elements, env)
		if len(elements) == 1 && isError(elements[0]) {
			return elements[0]
		}
		return &object.Array{Elements: elements}
	case *ast.IndexExpression:
		left := Eval(node.Left, env)
		if isError(left) {
			return left
		}
		index := Eval(node.Index, env)
		if isError(index) {
			return index
		}
		return evalIndexExpression(left, index, node.Token.LineNum)
	case *ast.HashLiteral:
		return evalHashLiteral(node, env)
	}
	return nil
}

func evalHashLiteral(node *ast.HashLiteral, env *object.Environment) object.Object {
	pairs := make(map[object.HashKey]object.HashPair)
	for keyNode, valueNode := range node.Pairs {
		key := Eval(keyNode, env)
		if isError(key) {
			return key
		}
		hashKey, ok := key.(object.Hashable)
		if !ok {
			return newError(node.Token.LineNum, "unusable as hash key: %s", key.Type())
		}
		value := Eval(valueNode, env)
		if isError(value) {
			return value
		}
		hashed := hashKey.HashKey()
		pairs[hashed] = object.HashPair{Key: key, Value: value}
	}
	return &object.Hash{Pairs: pairs}

}

func evalIndexExpression(left, index object.Object, lineNum int) object.Object {
	switch {
	case left.Type() == object.ARRAY_OBJ && index.Type() == object.INTEGER_OBJ:
		return evalArrayIndexExpression(left, index)
	case left.Type() == object.HASH_OBJ:
		return evalHashIndexExpression(left, index, lineNum)
	default:
		return newError(lineNum, "index operator not supported: %s", left.Type())
	}
}

func evalHashIndexExpression(hash, index object.Object, lineNum int) object.Object {
	hashObject := hash.(*object.Hash)
	key, ok := index.(object.Hashable)
	if !ok {
		return newError(lineNum, "unusable as hash key: %s", index.Type())
	}
	pair, ok := hashObject.Pairs[key.HashKey()]
	if !ok {
		return NULL
	}
	return pair.Value
}

func evalArrayIndexExpression(array, index object.Object) object.Object {
	arrayObject := array.(*object.Array)
	idx := index.(*object.Integer).Value
	max := int64(len(arrayObject.Elements) - 1)
	if idx < 0 || idx > max {
		return NULL
	}
	return arrayObject.Elements[idx]
}

// args为实参，且是经过表达式求值后得到的对象
func applyFunction(fn object.Object, args []object.Object, lineNum int) object.Object {
	switch fn := fn.(type) {
	case *object.Function:
		extendedEnv := extendFunctionEnv(fn, args)
		evaluated := Eval(fn.Body, extendedEnv)
		return unwrapReturnValue(evaluated)
		//如果函数的返回值是用return返回的，那么应当解包一次return，否则returnObj类型留到上层可能会产生意想不到的结果(会导致evalBlockExpression直接返回)
	case *object.Builtin:
		return fn.Fn(lineNum, args...)
	default:
		return newError(lineNum, "not a function: %s", fn.Type(), lineNum)
	}
}

func extendFunctionEnv(fn *object.Function, args []object.Object) *object.Environment {
	env := object.NewEnclosedEnvironment(fn.Env)
	for paramIdx, param := range fn.Parameters {
		env.Set(param.Value, args[paramIdx]) //把实参的值添加到新环境中绑定到形参上
	}
	return env
}

func unwrapReturnValue(obj object.Object) object.Object {
	if returnValue, ok := obj.(*object.ReturnValue); ok {
		return returnValue.Value
	}
	return obj
}

// 如果其中任意一个表达式产生了Error，那么只会返回含该error对象的长度为1的切片
func evalExpressions(exps []ast.Expression, env *object.Environment) []object.Object {
	var result []object.Object
	for _, e := range exps {
		evaluated := Eval(e, env)
		if isError(evaluated) {
			return []object.Object{evaluated}
		}
		result = append(result, evaluated)
	}
	return result
}

func evalIfExpression(exp *ast.IfExpression, env *object.Environment) object.Object {
	condition := Eval(exp.Condition, env)
	if isError(condition) {
		return condition
	}
	if isTrueObject(condition) {
		return Eval(exp.Consequence, env)
	} else if exp.Alternative == nil {
		return NULL
	} else {
		return Eval(exp.Alternative, env)
	}
}

func isTrueObject(obj object.Object) bool {
	switch obj {
	case NULL:
		return false
	case TRUE:
		return true
	case FALSE:
		return false
	default:
		if obj.Type() == object.INTEGER_OBJ {
			return obj.(*object.Integer).Value != 0
		}
		return true
	}
}

func evalInfixExpression(node *ast.InfixExpression, left, right object.Object) object.Object {
	operator := node.Operator
	switch {
	case left.Type() == object.INTEGER_OBJ && right.Type() == object.INTEGER_OBJ:
		return evalIntegerInfixExpression(operator, left, right, node.Token.LineNum)
	case left.Type() == object.STRING_OBJ && right.Type() == object.STRING_OBJ:
		return evalStringInfixExpression(operator, left, right, node.Token.LineNum)
	case operator == "==": //注意，这里直接比较指针，对于单例的Boolean和NULL可以比较，但对于两个Integer显然会出现预期外的结果，但这种情况已经在上一个case中被排除了
		return nativeBoolToBooleanObject(left == right)
	case operator == "!=":
		return nativeBoolToBooleanObject(left != right)
	case left.Type() != right.Type():
		return newError(node.Token.LineNum, "type mismatch: %s %s %s", left.Type(), operator, right.Type())
	default:
		//logger.Warningf("Comparing %s and %s, this will generate an Error!!!\n", left.Inspect(), right.Inspect())
		return newError(node.Token.LineNum, "unknown operator: %s %s %s", left.Type(), operator, right.Type())
	}
}

func evalStringInfixExpression(operator string, left, right object.Object, lineNum int) object.Object {
	leftVal := left.(*object.String).Value
	rightVal := right.(*object.String).Value
	switch operator {
	case "+":
		return &object.String{Value: leftVal + rightVal}
	case "==":
		return &object.Boolean{Value: leftVal == rightVal}
	case "!=":
		return &object.Boolean{Value: leftVal != rightVal}
	}
	return newError(lineNum, "unknown operator %s %s %s", left.Type(), operator, right.Type())
}

func evalIntegerInfixExpression(operator string, left, right object.Object, lineNum int) object.Object {
	leftVal := left.(*object.Integer).Value
	rightVal := right.(*object.Integer).Value
	var value int64
	switch operator {
	case "+":
		value = leftVal + rightVal
	case "-":
		value = leftVal - rightVal
	case "*":
		value = leftVal * rightVal
	case "/":
		if rightVal == 0 {
			return newError(lineNum, "divided by ZERO")
		}
		value = leftVal / rightVal
	case "%":
		if rightVal == 0 {
			return newError(lineNum, "divided by ZERO")
		}
		value = leftVal % rightVal
	case "<":
		return nativeBoolToBooleanObject(leftVal < rightVal)
	case ">":
		return nativeBoolToBooleanObject(leftVal > rightVal)
	case "==":
		return nativeBoolToBooleanObject(leftVal == rightVal)
	case "!=":
		return nativeBoolToBooleanObject(leftVal != rightVal)
	default:
		//logger.Warningf("Comparing %s and %s, this will generate a NULL!!!\n", left.Inspect(), right.Inspect())
		return newError(lineNum, "unknown operator:  %s %s %s", left.Type(), operator, right.Type())
	}
	return &object.Integer{Value: value}
}

func evalPrefixExpression(node *ast.PrefixExpression, right object.Object) object.Object {
	operator := node.Operator
	switch operator {
	case "!":
		return evalBangOperatorExpression(right)
	case "-":
		return evalMinusPrefixOperator(right, node.Token.LineNum)
	default:
		return newError(node.Token.LineNum, "unknown operator: %s%s", operator, right.Type())
	}
}

// 非数字时返回NULL 我认为应该报个错
func evalMinusPrefixOperator(right object.Object, lineNum int) object.Object {
	if right.Type() != object.INTEGER_OBJ {
		return newError(lineNum, "unknown operator: -%s", right.Type())
	}
	value := right.(*object.Integer).Value
	return &object.Integer{Value: -value} //返回一个新的object而不是修改原来的object
}

// BOOL规则：非0数字为true，非数字非bool非null为true
func evalBangOperatorExpression(right object.Object) object.Object {
	switch right {
	case TRUE:
		return FALSE
	case FALSE:
		return TRUE
	case NULL:
		return TRUE
	default:
		if right.Type() == object.INTEGER_OBJ {
			return nativeBoolToBooleanObject(right.(*object.Integer).Value == 0)
		}
		return FALSE
	}
}

func evalProgram(program *ast.Program, env *object.Environment) object.Object { //返回最后一个statement的值
	var result object.Object
	for _, statement := range program.Statements {
		result = Eval(statement, env)
		switch result := result.(type) {
		case *object.ReturnValue:
			return result.Value
		case *object.Error:
			return result // 遇到Error对象也得停止运行
		}
	}
	return result
}

func evalBlockStatement(block *ast.BlockStatement, env *object.Environment) object.Object { //如果没有return就返回最后一个statement的值
	var result object.Object
	for _, statement := range block.Statements {
		result = Eval(statement, env)
		if result != nil && (result.Type() == object.RETURN_VALUE_OBJ || result.Type() == object.ERROR_OBJ) { //处理return语句
			return result
		}
		//这里不要把ReturnValue解包为Object，以使得嵌套Block里的ReturnValue能被外层给识别到
		//if returnValue, ok := result.(*object.ReturnValue); ok { //处理return语句
		//	return returnValue.Value
		//}
	}
	return result
}

// 先在环境里找用户定义的标识符，再去找内建函数
func evalIdentifier(node *ast.Identifier, env *object.Environment) object.Object {
	if val, ok := env.Get(node.Value); ok {
		return val
	}
	if builtin, ok := builtins[node.Value]; ok {
		return builtin
	}
	return newError(node.Token.LineNum, "identifier not defined: "+node.Value)
}

func nativeBoolToBooleanObject(input bool) *object.Boolean {
	if input {
		return TRUE
	}
	return FALSE
}

func newError(lineNum int, format string, a ...interface{}) *object.Error {
	format = fmt.Sprintf("%s, at line %d", format, lineNum)
	return &object.Error{Message: fmt.Sprintf(format, a...)}
}

func isError(obj object.Object) bool {
	if obj != nil {
		return obj.Type() == object.ERROR_OBJ
	}
	return false
}
